package com.gitee.beiding.timer;

import java.util.Date;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;

/*
    单例模式
 */
public class MyTimer {

    private Dao dao;

    private Handlers handlers;

    MyTimer(Dao dao, Handlers handlers) {
        this.dao = dao;
        this.handlers = handlers;
    }

    //如果任务正在执行中,取消操作会失败
    public boolean cancel(String id) {
        return dao.remove(id);
    }

    public String immediate(String name, Object payload) {
        Logger.log("添加任务");
        return put(new Task(name, System.currentTimeMillis(), payload));
    }

    public String schedule(String name, Object payload, long delay) {
        return put(new Task(name, System.currentTimeMillis() + delay, payload));
    }

    public String schedule(String name, Object payload, Date time) {
        return put(new Task(name, time.getTime(), payload));
    }

    private TimerNode timerNode;

    //周期长度(大于1000)
    private int cycle = 3000;

    //视野倍数(大于等于3)
    private int viewMultiple = 10;

    private long len = cycle * viewMultiple;

    //最后的加载截止时间
    private static volatile long lastTime;

    private static final long initTime = System.currentTimeMillis();

    //初始化
    void start() {

        //使用CPU核心线程数作为执行者
        int timerNumber = Runtime.getRuntime().availableProcessors();

        //构建 Timer 环状结构
        TimerNode first = new TimerNode();
        timerNode = first;
        for (int i = 1; i < timerNumber; i++) {
            TimerNode t = new TimerNode();
            timerNode.next = t;
            timerNode = t;
        }
        timerNode.next = first;
        lastTime = initTime + viewMultiple * cycle;
        readTasks(0, lastTime);
        //心跳线程
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                beat();
            }
        }, cycle, cycle);
    }


    private void beat() {
        try {
            long preLastTime = lastTime;
            lastTime = System.currentTimeMillis() + len;
            readTasks(preLastTime, lastTime);
        } catch (Exception ignore) {
        }
    }

    private void readTasks(long start, long end) {
        Set<Task> tasks = dao.get(start, end);
        for (Task task : tasks) {
            long t = task.getTime() - System.currentTimeMillis();
            if (t < 0) {
                t = 0;
            }
            getTimer().schedule(createTimerTask(task), t);
        }
    }


    private void judgeAndPut(Task task) {
        if (task.getTime() < lastTime) {
            Logger.log("直接执行");
            dao.flagInMemory(task);
            submitTask(task);
        }
    }

    //放入一个任务
    private String put(Task task) {
        Logger.log("持久化任务");
        String id = dao.save(task);
        judgeAndPut(task);
        Logger.log("保存成功");
        return id;
    }

    private void submitTask(Task task) {
        long t = task.getTime() - System.currentTimeMillis();
        if (t < 0) {
            t = 0;
        }
        getTimer().schedule(createTimerTask(task), t);
    }

    private TimerTask createTimerTask(final Task task) {
        return new TimerTask() {
            @Override
            public void run() {
                //如果启动成功
                if (task.setStarting()) {
                    handlers.exe(task);
                    //判断是否需要重复执行
                    if (task.getAgainDelay() != null) {
                        task.setTime(System.currentTimeMillis() + task.getAgainDelay());
                        if (task.reset()) {
                            submitTask(task);
                        }
                    } else {
                        //该方法应该自行处理,可能会抛出异常
                        dao.remove(task.getId());
                    }
                }
            }
        };
    }

    //环形结构
    private Timer getTimer() {
        timerNode = timerNode.next;
        return timerNode.timer;
    }

    //辅助构建环状结构,实现多线程任务
    private static class TimerNode {
        private Timer timer = new Timer();
        private TimerNode next;
    }


}
